package com.lifp.handler;

import com.lifp.anno.MqttService;
import com.lifp.anno.MqttTopic;
import com.lifp.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 消息处理器
 * @author lifp
 * @date 2023/10/20
 */
@Component
@Slf4j
public class MqttMessageHandler implements MessageHandler {


    // 包含 @MqttService注解 的类(Component)
    public static Map<String, Object> mqttServices;

    @Autowired
    private MqttPahoMessageDrivenChannelAdapter adapter;

    @PostConstruct
    public void autoSubscribeImpl(){
        // 自动订阅系统
        // 初始化的时候 去订阅主题
        Set<String> topics = new HashSet<>(16);

        Map<String, Object> theMqttServices = getMqttServices();
        for(Map.Entry<String, Object> entry : theMqttServices.entrySet()){
            Class<?> clazz = entry.getValue().getClass();
            Method[] methods = clazz.getDeclaredMethods();
            for ( Method method: methods ){
                if (method.isAnnotationPresent(MqttTopic.class)){
                    MqttTopic handleTopic = method.getAnnotation(MqttTopic.class);
                    if(handleTopic.autoSubscribe()){
                        topics.add(handleTopic.value());
                    }
                }
            }
        }

        if(topics.size()>0){
            topics.forEach(item->{
                log.info("自动订阅主题: {}",item);
                adapter.addTopic(item);
            });
        }
    }

    public Map<String, Object> getMqttServices(){
        if(mqttServices==null){
            mqttServices = SpringUtils.getBeansByAnnotation(MqttService.class);
        }
        return mqttServices;
    }

    /**
     * 根据消息主题，匹配处理方法，并处理
     * @param message 消息
     * @return void
     * @author lifp
     * @date 2023/10/20
     **/
    public void matchAndHandleMessage(Message<?> message){
        // 在这里 我们根据不同的 主题 分发不同的消息

        List<Map<Class<?>,Method>> messageHandlerMethods = getMessageMatchedHandlerMethod(message);
        if(messageHandlerMethods.size() == 0){
            //如果没有匹配的，则默认处理
            log.info("没有匹配到处理器，统一处理"+message.getPayload());
        }

        messageHandlerMethods.forEach(item ->{
            for (Map.Entry<Class<?>, Method> proxy : item.entrySet()) {
                try {
                    proxy.getValue().invoke(SpringUtils.getBean(proxy.getKey()),message);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    log.error("代理炸了");
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                    log.error("执行 {} 方法出现错误",proxy.getValue().getName(),e);
                }
            }
        });
    }

    /**
     * 获取可以处理消息的方法
     * @param message 消息
     * @return java.util.List<java.lang.reflect.Method>
     * @author lifp
     * @date 2023/10/20
     **/
    private List<Map<Class<?>,Method>> getMessageMatchedHandlerMethod(Message<?> message) {
        List<Map<Class<?>,Method>> handlerList = new ArrayList<>();

        String receivedTopic = message.getHeaders().get("mqtt_receivedTopic",String.class);
        if(receivedTopic==null || "".equals(receivedTopic)){
            return handlerList;
        }

        for(Map.Entry<String, Object> entry : getMqttServices().entrySet()){
            // 把所有带有 @MqttService 的类遍历
            Class<?> clazz = entry.getValue().getClass();
            // 获取他所有方法
            Method[] methods = clazz.getDeclaredMethods();
            for ( Method method: methods ){
                if (method.isAnnotationPresent(MqttTopic.class)){
                    // 如果这个方法有 这个注解
                    MqttTopic handleTopic = method.getAnnotation(MqttTopic.class);
                    if(isMatch(receivedTopic,handleTopic.value())){
                        // 并且 这个 topic 匹配成功
                        Map map = new HashMap<>();
                        map.put(clazz, method);

                        handlerList.add(map);
                        /*try {
                            method.invoke(SpringUtils.getBean(clazz),message);
                            return;
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                            log.error("代理炸了");
                        } catch (InvocationTargetException e) {
                            log.error("执行 {} 方法出现错误",handleTopic.value(),e);
                        }*/
                    }
                }
            }
        }

        return handlerList;
    }


    /**
     * mqtt 订阅的主题与我实际的主题是否匹配
     * @param topic 是实际的主题
     * @param pattern 是我订阅的主题 可以是通配符模式
     * @return 是否匹配
     */
    public static boolean isMatch(String topic, String pattern){
        if((topic==null) || (pattern==null) ){
            return false;
        }

        if(topic.equals(pattern)){
            // 完全相等是肯定匹配的
            return true;
        }

        if("#".equals(pattern)){
            // # 号代表所有主题  肯定匹配的
            return true;
        }
        String[] splitTopic = topic.split("/");
        String[] splitPattern = pattern.split("/");

        boolean match = true;

        // 如果包含 # 则只需要判断 # 前面的
        for (int i = 0; i < splitPattern.length; i++) {
            if(!"#".equals(splitPattern[i])){
                // 不是# 号 正常判断
                if(i>=splitTopic.length){
                    // 此时长度不相等 不匹配
                    match = false;
                    break;
                }
                if(!splitTopic[i].equals(splitPattern[i]) && !"+".equals(splitPattern[i])){
                    // 不相等 且不等于 +
                    match = false;
                    break;
                }
            }
            else {
                // 是# 号  肯定匹配的
                break;
            }
        }

        return match;
    }

    @Override
    public void handleMessage(Message<?> message) throws MessagingException {
        matchAndHandleMessage(message);
    }
}
